package com.such.kitx.tax;

import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.such.kit.Callable;
import com.such.kit.Logger;
import com.such.kit.cache.CacheCommons;
import com.such.kit.datastructure.tree.TreeCommons;
import com.such.kit.datastructure.tree.bean.Node;
import com.such.kit.datastructure.tree.bean.Tree;
import com.such.kit.file.FileCommons;
import com.such.kit.file.JarFileCommons;
import com.such.kit.file.bean.FileInfo;
import com.such.kit.io.source.MemoryIOSource;
import com.such.kit.io.support.IOSource;
import com.such.kit.io.template.XmlIOTemplate;
import com.such.kit.placeholder.PlaceholderCommons;
import com.such.kit.stream.StreamCommons;
import com.such.kit.string.StringCommons;
import com.such.kit.validate.ValidateCommons;

/**
 * @author SUCH
 * <pre>
 * 税控接口辅助类，封装了一些通用工具方法
 * </pre>
 */
public class TaxHelper {

	/** DT_PARAM_DATA_NAME：数据的变量名，既 DATA_NAME.put、DATA_NAME.get */
	public static final String DT_PARAM_DATA_NAME = "DATA_NAME";

	private static String templateCache = CacheCommons.buildCacheName(TaxHelper.class, "template");
	private static String templatesCache = CacheCommons.buildCacheName(TaxHelper.class, "templates");
	private static String ioCache = CacheCommons.buildCacheName(TaxHelper.class, "io");
	private static Tree<FileInfo> versionTree = new Tree<FileInfo>();
	/** linkExpr：引用另外一个 xml 模板对应的数据模板的正则表达式规则，匹配格式为 #link[key] */
	private static String linkExpr = "\\#link\\[[0-9a-zA-Z\\.\\-\\_]+\\]";

	/**
	 * <pre>
	 * 获取指定厂商、版本下 xml 模板的完整路径
	 * </pre>
	 * @param taxVendor 税控服务厂商 [参考 TaxInterface.TAX_VENDOR_*]
	 * @param version 版本名 [参考 {@link TaxInterface}]
	 * @param template xml 模板名称
	 * @return xml 模板的完整路径
	 * @throws Exception 
	 */
	public static String getTemplatePath(final String taxVendor, final String version, final String template) throws Exception {
		initVersionTree(taxVendor);
		final String templatePath = FileCommons.appendPathNode(TaxInterface.TEMPLATE_ROOT_PATH, taxVendor, version, template);
		return CacheCommons.get(templateCache, templatePath, new Callable<String, String>() {
			public String call(String cacheName) throws Exception {
				FileInfo fileInfo = JarFileCommons.getAbsolutePath(templatePath);
				if (ValidateCommons.isNotEmpty(fileInfo)) {
					return templatePath;
				} else {
					Logger.debug(TaxHelper.class, StringCommons.merger("未找到模板 [", templatePath, "]，开始从母版本中查找"));
					Node<FileInfo> parentNode = versionTree.get(getVersionNodeParentId(taxVendor, version));
					while (ValidateCommons.isNotEmpty(parentNode)) {
						String parentVersion = parentNode.getTitle();
						String parentTemplatePath = FileCommons.appendPathNode(TaxInterface.TEMPLATE_ROOT_PATH, taxVendor, parentVersion, template);
						fileInfo = JarFileCommons.getAbsolutePath(parentTemplatePath);
						if (ValidateCommons.isNotEmpty(fileInfo)) {
							Logger.debug(TaxHelper.class, StringCommons.merger("在母版本中找到映射版本 [", templatePath , " > ", parentTemplatePath, "]"));
							return parentTemplatePath;
						}
						parentNode = versionTree.get(getVersionNodeParentId(taxVendor, parentVersion));
					}
					Logger.warn(TaxHelper.class, StringCommons.merger("未找到模板 [", templatePath, "]"));
					return "";
				}
			}
		});
	}

	/**
	 * <pre>
	 * 获取指定厂商、版本下所有 xml 模板的完整路径
	 * </pre>
	 * @param taxVendor 税控服务厂商 [参考 TaxInterface.TAX_VENDOR_*]
	 * @param version 版本名 [参考 {@link TaxInterface}]
	 * @return 厂商、版本下所有 xml 模板的完整路径
	 * @throws Exception
	 */
	public static List<String> getTemplatePaths(final String taxVendor, final String version) throws Exception {
		initVersionTree(taxVendor);
		final String versionPath = FileCommons.appendPathNode(TaxInterface.TEMPLATE_ROOT_PATH, taxVendor, version);
		return CacheCommons.get(templatesCache, versionPath, new Callable<String, List<String>>() {
			public List<String> call(String cacheName) throws Exception {
				Map<String, String> templateMap = new HashMap<String, String>();
				List<Node<FileInfo>> nodes = versionTree.getNodes(getVersionNodeId(taxVendor, version));
				for (Node<FileInfo> node : nodes) {
					String parentVersion = node.getTitle();
					String parentVersionPath = FileCommons.appendPathNode(TaxInterface.TEMPLATE_ROOT_PATH, taxVendor, parentVersion);
					List<FileInfo> fileInfos = JarFileCommons.listFolder(parentVersionPath);
					if (ValidateCommons.isNotEmpty(fileInfos)) {
						for (FileInfo fileInfo : fileInfos) {
							String template = fileInfo.getName();
							templateMap.put(template, FileCommons.appendPathNode(parentVersionPath, template));
						}
					}
				}
				return new ArrayList<String>(templateMap.values());
			}
		});
	}

	private static String getVersionNodeId(String taxVendor, String version) {
		return StringCommons.merger(taxVendor, version.substring(5, 9));
	}

	private static String getVersionNodeParentId(String taxVendor, String version) {
		return StringCommons.merger(taxVendor, version.substring(1, 5));
	}

	private static void initVersionTree(final String taxVendor) throws Exception {
		// 根节点 ID 为 0000，在 Tree 中 ID 要求是唯一的，因此将 taxVendor 作为 ID 前缀
		Node<FileInfo> versionNode = versionTree.get(getVersionNodeParentId(taxVendor, "V00000001"));
		if (ValidateCommons.isEmpty(versionNode)) {
			String vendorPath = FileCommons.appendPathNode(TaxInterface.TEMPLATE_ROOT_PATH, taxVendor);
			List<FileInfo> fileInfos = JarFileCommons.listFolder(vendorPath);
			versionTree.add(TreeCommons.createNodes(fileInfos, new Callable<FileInfo, String[]>() {
				public String[] call(FileInfo fileInfo) throws Exception {
					String name = fileInfo.getName();
					return new String[] {getVersionNodeId(taxVendor, name), getVersionNodeParentId(taxVendor, name), name};
				}
			}));
		}
	}

	/**
	 * <pre>
	 * 获取 xml 模板对应的 {@link XmlIOTemplate}
	 * XmlIOTemplate 中的 {@link IOSource} 为 {@link MemoryIOSource}
	 * 
	 * [警告] 实例化的 {@link XmlIOTemplate} 会进行缓存以提高效率，使用 {@link MemoryIOSource} 时应在合适的时候调用 {@link MemoryIOSource#clearInput()}、{@link MemoryIOSource#clearOutput()}
	 * </pre>
	 * @param templatePath xml 模板的相对路径
	 * @return XmlIOTemplate 实例
	 * @throws Exception
	 */
	public static XmlIOTemplate getXmlIOTemplate(final String templatePath) throws Exception {
		return CacheCommons.get(ioCache, templatePath, new Callable<String, XmlIOTemplate>() {
			public XmlIOTemplate call(String param) throws Exception {
				return new XmlIOTemplate(JarFileCommons.getResourceStream(templatePath));
			}
		});
	}

	/**
	 * <pre>
	 * [参考 {@link #getTemplatePath}] [参考 {@link #getXmlIOTemplate}]
	 * </pre>
	 */
	public static XmlIOTemplate getXmlIOTemplate(String taxVendor, String version, String template) throws Exception {
		return getXmlIOTemplate(getTemplatePath(taxVendor, version, template));
	}

	/**
	 * <pre>
	 * 输出数据模板中的预制内容到控制台，并可通过 {@link PlaceholderCommons 占位符} 引用 params 中的值
	 * 
	 * 可以通过 {@link #linkExpr #link[xml 模板]} 的 {@link PlaceholderCommons 占位符} 格式引用另外一个 xml 模板对应的数据模板
	 * 
	 * params 中约定的 key
	 *   {@link #DT_PARAM_DATA_NAME}
	 * </pre>
	 * @param taxVendor 税控服务厂商 [参考 TaxInterface.TAX_VENDOR_*]
	 * @param version 版本名 [参考 {@link TaxInterface}]
	 * @param template xml 模板名称
	 * @param params 参数集
	 * @throws Exception 
	 */
	public static void printDataTemplate(String taxVendor, String version, String template, Map<String, String> params) throws Exception {
		String dataTemplate = getDataTemplate(taxVendor, version, template, params);
		if (ValidateCommons.isEmpty(dataTemplate)) {
			String templatePath = FileCommons.appendPathNode(TaxInterface.TEMPLATE_ROOT_PATH, taxVendor, version, StringCommons.merger(template, TaxInterface.DATA_TEMPLATE_SUFFIX));
			System.out.println(StringCommons.merger("未找到数据模板 [", templatePath, "]"));
		} else {
			System.out.println(dataTemplate);
		}
	}

	private static String getDataTemplate(String taxVendor, String version, String template, Map<String, String> params) throws Exception {
		// 1. 首先检查指定版本目录下是否有对应的 xmld 文件
		String templatePath = FileCommons.appendPathNode(TaxInterface.TEMPLATE_ROOT_PATH, taxVendor, version, StringCommons.merger(template, TaxInterface.DATA_TEMPLATE_SUFFIX));
		FileInfo fileInfo = JarFileCommons.getAbsolutePath(templatePath);
		if (ValidateCommons.isEmpty(fileInfo)) {
			// 2. 如果当前版本目录下没有，则通过 getTemplatePath 方法在其继承版本中查找
			templatePath = StringCommons.merger(getTemplatePath(taxVendor, version, template), TaxInterface.DATA_TEMPLATE_SUFFIX);
			fileInfo = JarFileCommons.getAbsolutePath(templatePath);
		}
		if (ValidateCommons.isNotEmpty(fileInfo)) {
			String dataTemplate = StreamCommons.textReader(new InputStreamReader(JarFileCommons.getResourceStream(templatePath)));

			List<String> linkKeys = PlaceholderCommons.getKeys(linkExpr, dataTemplate, 6, 1);
			if (ValidateCommons.isNotEmpty(linkKeys)) {
				Map<String, String> linkParams = new HashMap<String, String>();
				for (String linkKey : linkKeys) {
					String linkTemplate = getDataTemplate(taxVendor, version, linkKey, params);
					if (ValidateCommons.isNotEmpty(linkTemplate)) {
						linkParams.put(linkKey, linkTemplate);
					}
				}
				dataTemplate = PlaceholderCommons.replacePlaceholder(linkExpr, dataTemplate, 6, 1, linkParams, true);
			}

			dataTemplate = PlaceholderCommons.replacePlaceholder(dataTemplate, params);
			return dataTemplate;
		} else {
			return "";
		}
	}

}
